<template>
  <AForm
    v-bind="getBindValue"
    :class="getFormClass"
    ref="formElRef"
    :model="formModel"
    @keypress.enter="handleEnterPress"
  >
    <Row
      :class="
        advanceState.isAdvanced && !advanceState.hideAdvanceBtn
          ? 'suspensionRow'
          : advanceState.hideAdvanceBtn
          ? 'relative !block '
          : ''
      "
    >
      <Row v-bind="getRow">
        <slot name="formHeader"></slot>
        <template v-for="(schema, index) in getSchema" :key="schema.field">
          <template v-if="schema?.title">
            <div class="form-header">{{ schema.title }}</div>
            <Divider />
          </template>
          <template v-if="schema?.subTitle">
            <div class="form-subTitle">{{ schema.subTitle }}</div>
          </template>

          <template v-if="schema.component === 'Grid'">
            <!-- <Row type="flex" :gutters="0" justify="start" align="top"> -->
            <ACol v-for="(col, colIndex) in schema.children" :key="colIndex" :span="col.span">
              <FormItem
                v-for="(colItem, idx) in col.list"
                :key="idx"
                :tableAction="tableAction"
                :formActionType="formActionType"
                :schema="colItem"
                :formProps="getProps"
                :allDefaultValues="defaultValueRef"
                :formModel="formModel"
                :setFormModel="setFormModel"
              >
                <template #[item]="data" v-for="item in Object.keys($slots)">
                  <slot :name="item" v-bind="data || {}"></slot>
                </template>
              </FormItem>
            </ACol>
            <!-- </Row> -->
          </template>
          <template v-if="schema.component === 'Tab'">
            <ACol :span="24">
              <Tabs
                v-model:activeKey="activeKey[index]"
                :tabPosition="(schema.componentProps as any ).tabPosition"
                :size="(schema.componentProps as any ).tabSize"
              >
                <TabPane v-for="(tab, tabIndex) in schema.children" :tab="tab.name" :key="tabIndex">
                  <FormItem
                    v-for="(tabItem, idx) in tab.list"
                    :key="idx"
                    :tableAction="tableAction"
                    :formActionType="formActionType"
                    :schema="tabItem"
                    :formProps="getProps"
                    :allDefaultValues="defaultValueRef"
                    :formModel="formModel"
                    :setFormModel="setFormModel"
                  >
                    <template #[item]="data" v-for="item in Object.keys($slots)">
                      <slot :name="item" v-bind="data || {}"></slot>
                    </template>
                  </FormItem>
                </TabPane>
              </Tabs>
            </ACol>
          </template>
          <FormItem
            v-else
            :tableAction="tableAction"
            :formActionType="formActionType"
            :schema="schema"
            :formProps="getProps"
            :allDefaultValues="defaultValueRef"
            :formModel="formModel"
            :setFormModel="setFormModel"
          >
            <template #[item]="data" v-for="item in Object.keys($slots)">
              <slot :name="item" v-bind="data || {}"></slot>
            </template>
          </FormItem>
        </template>
      </Row>
      <FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced">
        <template
          #[item]="data"
          v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"
        >
          <slot :name="item" v-bind="data || {}"></slot>
        </template>
      </FormAction>
      <slot name="formFooter"></slot>
    </Row>
  </AForm>
</template>
<script lang="ts">
  import type { FormActionType, FormProps, FormSchema } from './types/form';
  import type { AdvanceState } from './types/hooks';
  import type { Ref } from 'vue';

  import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue';
  import { Form, Row, Col, Tabs, TabPane, Divider } from 'ant-design-vue';
  import FormItem from './components/FormItem.vue';
  import FormAction from './components/FormAction.vue';

  import { dateItemType } from './helper';
  import { dateUtil } from '/@/utils/dateUtil';

  // import { cloneDeep } from 'lodash-es';
  import { deepMerge } from '/@/utils';

  import { useFormValues } from './hooks/useFormValues';
  import useAdvanced from './hooks/useAdvanced';
  import { useFormEvents } from './hooks/useFormEvents';
  import { createFormContext } from './hooks/useFormContext';
  import { useAutoFocus } from './hooks/useAutoFocus';
  import { useModalContext } from '/@/components/Modal';
  import { useDebounceFn } from '@vueuse/core';

  import { basicProps } from './props';
  import { useDesign } from '/@/hooks/web/useDesign';

  export default defineComponent({
    name: 'BasicForm',
    components: { FormItem, AForm: Form, Row, ACol: Col, Tabs, TabPane, FormAction, Divider },
    props: basicProps,
    emits: ['advanced-change', 'reset', 'submit', 'register', 'field-value-change'],
    setup(props, { emit, attrs }) {
      const formModel = reactive<Recordable>({});
      const modalFn = useModalContext();

      const advanceState = reactive<AdvanceState>({
        isAdvanced: true,
        hideAdvanceBtn: true,
        isLoad: true,
        actionSpan: 6,
      });

      const defaultValueRef = ref<Recordable>({});
      const isInitedDefaultRef = ref(false);
      const propsRef = ref<Partial<FormProps>>({});
      const schemaRef = ref<Nullable<FormSchema[]>>(null);
      const formElRef = ref<Nullable<FormActionType>>(null);
      const activeKey = ref([]);

      const { prefixCls } = useDesign('basic-form');

      // Get the basic configuration of the form
      const getProps = computed((): FormProps => {
        return { ...props, ...unref(propsRef) } as FormProps;
      });

      const getFormClass = computed(() => {
        return [
          prefixCls,
          'relative',
          {
            [`${prefixCls}--compact`]: unref(getProps).compact,
          },
          advanceState.isAdvanced ? '' : 'overflow-hidden',
        ];
      });

      // Get uniform row style and Row configuration for the entire form
      const getRow = computed((): Recordable => {
        const { baseRowStyle = {}, rowProps } = unref(getProps);
        let classStr =
          unref(getProps).showAdvancedButton && !advanceState.hideAdvanceBtn
            ? 'advanceRow relative !ml-0 !mr-0'
            : unref(getProps).showAdvancedButton
            ? 'basicRow relative !ml-0 !mr-0'
            : 'flex-1';
        return {
          style: baseRowStyle,
          ...rowProps,
          class: classStr,
        };
      });

      const getBindValue = computed(
        () => ({ ...attrs, ...props, ...unref(getProps) } as Recordable),
      );

      const getSchema = computed((): FormSchema[] => {
        const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);

        for (const schema of schemas) {
          const { defaultValue, component } = schema;

          // handle date type
          if (defaultValue && dateItemType.includes(component)) {
            if (!Array.isArray(defaultValue)) {
              schema.defaultValue = dateUtil(defaultValue);
            } else {
              const def: any[] = [];
              defaultValue.forEach((item) => {
                def.push(dateUtil(item));
              });
              schema.defaultValue = def;
            }
          }
        }

        if (unref(getProps).showAdvancedButton) {
          return schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[];
        } else {
          return schemas as FormSchema[];
        }
      });

      const { handleToggleAdvanced, searchWidth } = useAdvanced({
        advanceState,
        emit,
        getProps,
        getSchema,
        formModel,
        defaultValueRef,
      });

      const { handleFormValues, initDefault } = useFormValues({
        getProps,
        defaultValueRef,
        getSchema,
        formModel,
      });

      useAutoFocus({
        getSchema,
        getProps,
        isInitedDefault: isInitedDefaultRef,
        formElRef: formElRef as Ref<FormActionType>,
      });

      const {
        handleSubmit,
        clearValidate,
        validate,
        validateFields,
        getFieldsValue,
        updateSchema,
        resetSchema,
        appendSchemaByField,
        removeSchemaByFiled,
        resetFields,
        setFieldsValue,
        scrollToField,
      } = useFormEvents({
        emit,
        getProps,
        formModel,
        getSchema,
        defaultValueRef,
        formElRef: formElRef as Ref<FormActionType>,
        schemaRef: schemaRef as Ref<FormSchema[]>,
        handleFormValues,
      });

      createFormContext({
        resetAction: resetFields,
        submitAction: handleSubmit,
      });

      watch(
        () => unref(getProps).model,
        () => {
          const { model } = unref(getProps);
          if (!model) return;
          setFieldsValue(model);
        },
        {
          immediate: true,
        },
      );

      watch(
        () => unref(getProps).schemas,
        (schemas) => {
          resetSchema(schemas ?? []);
        },
      );

      watch(
        () => getSchema.value,
        (schema) => {
          nextTick(() => {
            //  Solve the problem of modal adaptive height calculation when the form is placed in the modal
            modalFn?.redoModalHeight?.();
          });
          if (unref(isInitedDefaultRef)) {
            return;
          }
          if (schema?.length) {
            initDefault();
            isInitedDefaultRef.value = true;
          }
        },
      );

      watch(
        () => formModel,
        useDebounceFn(() => {
          unref(getProps).submitOnChange && handleSubmit();
        }, 300),
        { deep: true },
      );

      async function setProps(formProps: Partial<FormProps>): Promise<void> {
        propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
      }

      function setFormModel(key: string, value: any) {
        formModel[key] = value;
        const { validateTrigger } = unref(getBindValue);
        if (!validateTrigger || validateTrigger === 'change') {
          validateFields([key]).catch((_) => {});
        }
        emit('field-value-change', key, value);
      }

      function handleEnterPress(e: KeyboardEvent) {
        const { autoSubmitOnEnter } = unref(getProps);
        if (!autoSubmitOnEnter) return;
        if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
          const target: HTMLElement = e.target as HTMLElement;
          if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
            handleSubmit();
          }
        }
      }

      const formActionType: Partial<FormActionType> = {
        getFieldsValue,
        setFieldsValue,
        resetFields,
        updateSchema,
        resetSchema,
        setProps,
        removeSchemaByFiled,
        appendSchemaByField,
        clearValidate,
        validateFields,
        validate,
        submit: handleSubmit,
        scrollToField: scrollToField,
      };

      onMounted(() => {
        initDefault();
        emit('register', formActionType);
      });

      return {
        getBindValue,
        handleToggleAdvanced,
        handleEnterPress,
        formModel,
        defaultValueRef,
        advanceState,
        getRow,
        getProps,
        formElRef,
        getSchema,
        formActionType: formActionType as any,
        setFormModel,
        activeKey,
        getFormClass,
        getFormActionBindProps: computed(
          (): Recordable => ({ ...getProps.value, ...advanceState }),
        ),
        ...formActionType,
        searchWidth,
      };
    },
  });
</script>
<style lang="less">
  @prefix-cls: ~'@{namespace}-basic-form';

  .@{prefix-cls} {
    .ant-form-item {
      &-label {
        label {
          &::after {
            margin: 0 6px 0 2px;
          }
        }
      }

      &-with-help {
        margin-bottom: 0;
      }

      &:not(.ant-form-item-with-help) {
        margin-bottom: 25px;
      }

      &.suffix-item {
        .ant-form-item-children {
          display: flex;
        }

        .ant-form-item-control {
          margin-top: 4px;
        }

        .suffix {
          display: inline-flex;
          padding-left: 6px;
          margin-top: 1px;
          line-height: 1;
          align-items: center;
        }
      }

      .ant-input-number,
      .ant-picker {
        width: 100%;
      }

      .ant-input-affix-wrapper {
        padding: 4px 6px;
      }

      .ant-select-selector {
        padding: 0 6px;
      }
    }

    .ant-form-explain {
      font-size: 14px;
    }

    &--compact {
      .ant-form-item {
        margin-bottom: 8px !important;
      }
    }

    .form-header {
      margin: 5px 0 -15px;
      font-size: 16px;
      line-height: 18px;
      padding-left: 6px;
      border-left: 6px solid #5e95ff;
    }

    .form-subTitle {
      font-weight: 700;
      margin: 0 0 10px 35px;
    }

    .advanceRow {
      width: calc(100% - 248px);
      align-content: flex-start;
      overflow: auto;
      max-height: 380px;
    }

    .basicRow {
      width: calc(100% - 178px);
    }

    .suspensionRow {
      position: absolute;
      max-height: 400px;
      z-index: 6;
      background: #fff;
      border: 1px solid rgb(204 204 204 / 47%);
      box-shadow: 1px 2px 10px #ccc;
      top: 0;
      left: 0;
      right: 0;
      padding: 8px 0;
      align-content: flex-start;
    }
  }

  @media (max-width: @screen-sm) {
    .ant-form {
      .ant-form-item {
        flex-wrap: nowrap;

        .ant-form-item-label {
          flex: none;
        }

        .ant-form-item-control {
          flex: auto;
        }
      }
    }
  }

  @media screen {
    .@{prefix-cls} {
      .basicRow {
        width: v-bind('searchWidth.xs.row');
      }

      .basicCol {
        left: v-bind('searchWidth.xs.col');
      }
    }
  }
  @media (min-width: @screen-sm) {
    .@{prefix-cls} {
      .basicRow {
        width: v-bind('searchWidth.sm.row');
      }

      .basicCol {
        left: v-bind('searchWidth.sm.col');
      }
    }
  }
  @media (min-width: @screen-md) {
    .@{prefix-cls} {
      .basicRow {
        width: v-bind('searchWidth.md.row');
      }

      .basicCol {
        left: v-bind('searchWidth.md.col');
      }
    }
  }
  @media (min-width: @screen-lg) {
    .@{prefix-cls} {
      .basicRow {
        width: v-bind('searchWidth.lg.row');
      }

      .basicCol {
        left: v-bind('searchWidth.lg.col');
      }
    }
  }
</style>
